package javaer.starter.base.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Sets;
import javaer.starter.base.dao.DependencyDAO;
import javaer.starter.base.dao.DependencyExcludedDAO;
import javaer.starter.base.dao.DependencyRelatedDAO;
import javaer.starter.base.domain.dto.DependencyDTO;
import javaer.starter.base.domain.dto.DependencyExcludedDTO;
import javaer.starter.base.domain.model.DependencyExcludedModel;
import javaer.starter.base.domain.model.DependencyModel;
import javaer.starter.base.domain.model.DependencyRelatedModel;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class DependencyService extends ServiceImpl<DependencyDAO, DependencyModel> {

    @Resource
    private DependencyDAO dependencyDAO;

    @Resource
    private DependencyRelatedDAO dependencyRelatedDAO;

    @Resource
    private DependencyExcludedDAO dependencyExcludedDAO;

    public Set<DependencyDTO> findByTypeIdAndDepIds(String typeId, Set<String> dependencyIds, Set<String> loaded) {
        if (loaded == null) {
            loaded = new HashSet<>();
        }
        if (CollectionUtils.isEmpty(dependencyIds)) {
            return Sets.newHashSet();
        }
        loaded.addAll(dependencyIds);
        // 查询所有依赖
        List<DependencyModel> dependencyModels = dependencyDAO.selectList(new QueryWrapper<DependencyModel>()
                .lambda()
                .eq(DependencyModel::getTypeId, typeId)
                .in(DependencyModel::getDepId, dependencyIds)
        );

        if (CollectionUtils.isEmpty(dependencyModels)) {
            return null;
        }
        Set<DependencyDTO> result = new HashSet<>(dependencyModels.size());

        // 查询依赖关联的依赖+查询依赖排除的依赖
        for (DependencyModel dependencyModel : dependencyModels) {
            DependencyDTO dependencyDTO = new DependencyDTO();
            BeanUtils.copyProperties(dependencyModel, dependencyDTO);
            // 查询所有关联的依赖
            List<DependencyRelatedModel> dependencyRelatedModels = dependencyRelatedDAO.selectList(new QueryWrapper<DependencyRelatedModel>()
                    .lambda()
                    .eq(DependencyRelatedModel::getTypeId, typeId)
                    .eq(DependencyRelatedModel::getDepId, dependencyModel.getDepId())
            );

            if (CollectionUtils.isNotEmpty(dependencyRelatedModels)) {
                Set<String> relatedDepId = dependencyRelatedModels.stream().map(drm -> drm.getRelatedDepId()).collect(Collectors.toSet());
                relatedDepId.removeAll(loaded);
                Set<DependencyDTO> recursionDeps = findByTypeIdAndDepIds(typeId, relatedDepId, loaded);
                if (!recursionDeps.isEmpty()) {
                    result.addAll(recursionDeps);
                }
            }

            List<DependencyExcludedModel> dependencyExcludedModels = dependencyExcludedDAO.selectList(new QueryWrapper<DependencyExcludedModel>()
                    .lambda()
                    .eq(DependencyExcludedModel::getTypeId, typeId)
                    .eq(DependencyExcludedModel::getDepId, dependencyModel.getDepId())
            );

            if (CollectionUtils.isNotEmpty(dependencyExcludedModels)) {
                Set<DependencyExcludedDTO> excludedDTOSet =
                        dependencyExcludedModels
                                .stream()
                                .map(model -> {
                                    DependencyExcludedDTO dependencyExcludedDTO = new DependencyExcludedDTO();
                                    BeanUtils.copyProperties(model, dependencyExcludedDTO);
                                    return dependencyExcludedDTO;
                                }).collect(Collectors.toSet());
                dependencyDTO.setExclusions(excludedDTOSet);
            }
            result.add(dependencyDTO);
        }
        return result;
    }

    public Set<DependencyDTO> findByTypeIdAndDepIds(String typeId, Set<String> dependencyIds) {
        return findByTypeIdAndDepIds(typeId, dependencyIds, null);
    }

    public DependencyDTO findByTypeIdAndDepId(String typeId, String dependencyId) {
        Set<String> dependencyIds = new HashSet<>();
        dependencyIds.add(dependencyId);
        Set<DependencyDTO> dependencyDTOS = findByTypeIdAndDepIds(typeId, dependencyIds);
        if (CollectionUtils.isNotEmpty(dependencyDTOS)) {
            return dependencyDTOS.stream().findFirst().get();
        }
        return null;
    }
}
